<?php

namespace App\Console\Scripts\Counter;

use App\Common\Libs\ArrayHelper;
use App\Consts\GlobalConst;
use App\Consts\GlobalCounterConst;
use App\Consts\GlobalTopicConst;
use App\Models\Cache\Cache;
use App\Models\Cache\CacheKey;
use App\Models\FakeTopic;
use App\Models\SyslogFrontend;
use App\Models\SysTopic;
use Illuminate\Database\Query\Builder;
use Illuminate\Support\Facades\DB;

/**
 * 话题计数统计脚本
 * Class ArticleCounterCommand
 * @package App\Console\Scripts\Counter
 */
class TopicCounterCommand extends BaseCounterCommand
{

    /**
     * The name and signature of the console command.
     *
     * @var string
     */
    protected $signature = 'counter:topic {action} {--force=}';

    /**
     * The console command description.
     *
     * @var string
     */
    protected $description = 'counter for topic';

    /**
     * 话题热度统计 - 真热度
     * [!] 计数是累计形式，若是强制刷新则全统计
     * [!] 伴随定时任务，会反复执行
     * [!] 先获取上次执行结果和时间，再判断是否需要执行
     * @return int
     */
    public function hot()
    {
        $subMode = $this->_getSubModel('hot');
        // 防重复执行
        if (false === $subMode) {
            $this->info('[topic/hot] nothing to update .');
            return 0;
        }
        $finish = $subMode->count();
        // 每 100 单位合并处理一次
        $subMode->chunkById(100, function ($list) use ($finish) {
            foreach ($list as $item) {
                static $index = 1;
                $id = $item->topic_id;

                // 增量更新
                $latest = Cache::getInstance(['topic_id' => '_latest'])->hGetAll(CacheKey::COUNTER_TOPIC);
                $updateTime = $latest['hot_update_time'] ?? 0;
                $total = $this->_getLogSubModel(function (Builder $query) use ($id, $updateTime) {
                    $query->where('api_route', '/api/topic/detail');
                    $query->where('params', 'like', "%{$id}%");
                    $query->where('created_at', '>', $updateTime);
                }, ['id'], false)->count() ? : 0;
                $oldCount = 0;
                if (!$this->option('force') && $updateTime) {
                    $latest = Cache::getInstance(['topic_id' => $id])->hGetAll(CacheKey::COUNTER_TOPIC);
                    $oldCount = $latest[GlobalCounterConst::COUNT_HOT] ?? 0;
                }

                $total =  $oldCount + $total * GlobalTopicConst::HOT_TIMES;
                // set cache
                $cacheKey = $this->setHashCache(
                    ['topic_id' => $id],
                    CacheKey::COUNTER_TOPIC,
                    [GlobalCounterConst::COUNT_HOT => $total]
                );
                // log info
                $info = "[$index/$finish] set cache key `$cacheKey` - ".GlobalCounterConst::COUNT_HOT." {$this->sort} to (int) $total";
                $this->info($info);
                if ($total) $this->logger($info, false, 'script');
                $index++;
            }
        }, 'topic_id');

        // 更新缓存，防止重复执行
        $log = $this->_getLogSubModel(function ($query) {
            $query->where('api_route', '/api/topic/detail');
        }, ['id', 'created_at'])->orderByDesc('created_at')->first();
        $this->setHashCache(
            ['topic_id' => '_latest'],
            CacheKey::COUNTER_TOPIC,
            [
                'hot_update_id' => $log->id ?? 0,
                'hot_update_time' => $log->created_at ?? 0,
            ]
        );

        return 1;
    }

    /**
     * 话题热度统计 - 假热度
     * @return int
     */
    public function fake()
    {
        $subMode = $this->_getSubModel('fake');
        // 防重复执行
        if (false === $subMode) {
            $this->info('[topic/fake] nothing to update .');
            return 0;
        }
        $finish = $subMode->count();
        // 每 100 单位合并处理一次
        $subMode->chunkById(100, function ($list) use ($finish) {
            foreach ($list as $item) {
                static $index = 1;
                $id = $item->topic_id;
                $total = FakeTopic::getModel()
                    ->where('topic_id', $id)
                    ->where('action_type', GlobalCounterConst::ACTION_TYPE_TOPIC_VIEW)
                    ->value('count') ? : 0;
                // set cache
                $cacheKey = $this->setHashCache(
                    ['topic_id' => $id],
                    CacheKey::COUNTER_TOPIC,
                    [GlobalCounterConst::FAKE_HOT => $total]
                );
                // log info
                $info = "[$index/$finish] set cache key `$cacheKey` - ".GlobalCounterConst::FAKE_HOT." {$this->sort} to (int) $total";
                $this->info($info);
                if ($total) $this->logger($info, false, 'script');
                $index++;
            }
        }, 'topic_id');

        // 更新缓存，防止重复执行
        $total = FakeTopic::getModel()
            ->where('action_type', GlobalCounterConst::ACTION_TYPE_TOPIC_VIEW)
            ->sum('count') ? : 0;
        $this->setHashCache(
            ['topic_id' => '_latest'],
            CacheKey::COUNTER_TOPIC,
            ['fake_total_count' => $total]
        );

        return 1;
    }

    /**
     * get sub model
     * @param $type
     * @return \Illuminate\Database\Eloquent\Builder|\Illuminate\Database\Eloquent\Model|mixed
     */
    private function _getSubModel($type = 'hot')
    {

        // 是否强制执行，若是则先清除缓存
        $force = $this->option('force') ? : false;
        if ($force) {
            Cache::getInstance(['topic_id' => '_latest'])->delete(CacheKey::COUNTER_TOPIC);
        }

        // 先获取缓存，根据缓存判断是否需要执行, 没有则赋默认值
        $latest = Cache::getInstance(['topic_id' => '_latest'])->hGetAll(CacheKey::COUNTER_TOPIC);
        $latest['hot_update_id'] = $latest['hot_update_id'] ?? 0;
        $latest['hot_update_time'] = $latest['hot_update_time'] ?? 0;
        $latest['fake_total_count'] = $latest['fake_total_count'] ?? 0;

        if ('hot' == $type) {
            // 判断 hot 是否需要执行
            $isCurrent = date('Y-m', $latest['hot_update_time']) == date('Y-m');
            $log = $this->_getLogSubModel(function ($query) {
                $query->where('api_route', '/api/topic/detail');
            }, ['id', 'created_at'], $isCurrent)->orderByDesc('created_at')->first();
            if ($log && $log->id == $latest['hot_update_id'] && $log->created_at == $latest['hot_update_time']) {
                return false;
            }
        }

        if ('fake' == $type) {
            // 判断 fake 是否需要执行
            // 没有更新时间，只能通过总数变化去判断是否有更新了
            $total = FakeTopic::getModel()
                ->where('action_type', GlobalCounterConst::ACTION_TYPE_TOPIC_VIEW)
                ->sum('count') ? : 0;
            if ($total == $latest['fake_total_count']) {
                return  false;
            }
        }

        // 返回查询对象
        $subMode = SysTopic::getSubModel(function (Builder $query) {
            $query->where('is_deleted', GlobalConst::IS_NOT_DELETE);
        }, ['id', 'topic_id']);

        return $subMode;
    }

    /**
     * @param array|mixed $where
     * @param array $field
     * @param bool $isCurrent
     * @return SyslogFrontend|\Illuminate\Database\Eloquent\Builder|\Illuminate\Database\Eloquent\Model|Builder
     */
    private function _getLogSubModel($where, $field = ['*'], $isCurrent = true)
    {
        // 当月， 返回单表模型
        if ($isCurrent) {
            $tableName = SyslogFrontend::getTableNameByMonth(time());
            return SyslogFrontend::setTableName($tableName)->select($field)->where($where);
        }

        // 非当月，返回连表模型
        $tables = DB::select('show tables like "syslog_frontend_%"');
        $tables = ArrayHelper::getColumn($tables, 'Tables_in_' . env('DB_DATABASE') . " (syslog_frontend_%)");

        $model = DB::table($tables[0])->select($field)->where($where);
        foreach ($tables as $key => $table) {
            if (!$key) continue;
            $subQuery = DB::table($table)->select($field)->where($where);
            $model = $model->union($subQuery, true);
        }
        $subSql = getSql($model);
        return DB::table(DB::raw("({$subSql}) as subTable"));
    }

}
